#include "hook.h"
#include <dlfcn.h>
#include <cstdarg>
#include <string>
#include <functional>
#include "fd_manager.h"
#include "fiber.h"
#include "iomanager.h"

namespace fiber 
{
// 当前线程是否启用hook
static thread_local bool t_hook_enable = false;
static int g_tcp_connect_timeout = 5000;    // tcp连接超时时间5s

// 定义需要Hook的系统函数列表
// C/C++ 预处理器宏的 “参数化列表展开” 技巧
#define HOOK_FUN(XX)   \
    XX(sleep)          \
    XX(usleep)         \
    XX(nanosleep)      \
    XX(socket)         \
    XX(connect)        \
    XX(accept)         \
    XX(read)           \
    XX(readv)          \
    XX(recv)           \
    XX(recvfrom)       \
    XX(recvmsg)        \
    XX(write)          \
    XX(writev)         \
    XX(send)           \
    XX(sendto)         \
    XX(sendmsg)        \
    XX(close)          \
    XX(fcntl)          \
    XX(ioctl)          \
    XX(getsockopt)     \
    XX(setsockopt)

// 初始化Hook：获取原始系统函数地址
void hook_init() {
    static bool is_inited = false;
    if (is_inited) {
        return;
    }
// dlsym:Dynamic LinKinf Library.返回指定符号的地址
#define XX(name) name##_f = (name##_fun)dlsym(RTLD_NEXT, #name);
    HOOK_FUN(XX);
#undef XX
}

// hook_init放在静态对象中，则在main函数执行之前就会获取各个符号地址并
// 保存到全局变量中
static uint64_t s_connect_timeout = -1;
struct _HOOKIniter {
    _HOOKIniter() {
        hook_init();
        s_connect_timeout = g_tcp_connect_timeout;
        // std::cout << "hook init success" << std::endl;
    }
};
// 静态对象初始化，确保main函数前完成Hook初始化
static _HOOKIniter s_hook_initer;

bool is_hook_enable() { return t_hook_enable; }

void set_hook_enable(const bool flag) { t_hook_enable = flag; }

struct timer_info {
    int cnacelled = 0;
};

/**
 * @brief 通用 IO 操作钩子模板
 * @tparam OriginFun 原始系统函数的类型（函数指针）
 * @tparam Args 可变参数类型（系统函数的参数类型）
 * @param fd 文件描述符
 * @param fun 原始系统函数（如 read_f、write_f 等）
 * @param hook_fun_name 钩子函数名称（用于日志输出）
 * @param event IO 事件类型（READ 或 WRITE，用于 IO 管理器注册）
 * @param timeout_so 超时选项（如 SO_RCVTIMEO 表示读超时，SO_SNDTIMEO 表示写超时）
 * @param args 传递给原始系统函数的参数（完美转发）
 * @return 系统函数的返回值（如读取/写入的字节数，失败返回-1）
 */
template <typename OriginFun, typename... Args>
static ssize_t do_io(int fd, OriginFun fun, const char *hook_fun_name, uint32_t event, int timeout_so, Args &&...args) {
    // 1. 若当前线程未启用 Hook，直接调用原始系统函数
    if (!t_hook_enable) {
        // 完美转发参数，确保参数传递的效率和正确性
        return fun(fd, std::forward<Args>(args)...);
    }

    // 2. 获取文件描述符的上下文（管理 fd 的状态：是否为 socket、阻塞模式、超时时间等）
    FdCtx::ptr ctx = FdMgr::GetInstance()->get(fd);
    // 若上下文不存在（未被管理），直接调用原始函数
    if (!ctx) {
        return fun(fd, std::forward<Args>(args)...);
    }

    // 3. 检查文件是否已关闭，若关闭则设置错误码并返回
    if (ctx->isClose()) {
        errno = EBADF;  // EBADF 表示无效的文件描述符
        return -1;
    }

    // 4. 非 socket 或用户显式设置为非阻塞模式，直接调用原始函数
    // （仅对 socket 的阻塞模式操作进行 Hook 转换）
    if (!ctx->isSocket() || ctx->getUserNonblock()) {
        return fun(fd, std::forward<Args>(args)...);
    }

    // 5. 获取该 IO 操作的超时时间（从上下文读取预设的超时配置）
    uint64_t to = ctx->getTimeout(timeout_so);
    // 用于跟踪超时状态的共享指针（定时器回调中修改）
    std::shared_ptr<timer_info> tinfo(new timer_info);

retry:  // 重试标签：事件就绪或超时后重新执行 IO 操作
    // 6. 调用原始系统函数执行 IO 操作
    ssize_t n = fun(fd, std::forward<Args>(args)...);

    // 7. 若操作被信号中断（EINTR），循环重试
    while (n == -1 && errno == EINTR) {
        n = fun(fd, std::forward<Args>(args)...);
    }

    // 8. 若操作返回 EAGAIN（数据未就绪，非阻塞模式下的正常返回）
    if (n == -1 && errno == EAGAIN) {
        // 8.1 获取当前线程的 IO 管理器（负责事件监听和协程调度）
        IOManager* iom = IOManager::GetThis();
        Timer::ptr timer;  // 定时器指针（用于超时控制）
        // 弱指针（避免定时器回调持有 shared_ptr 导致的循环引用）
        std::weak_ptr<timer_info> winfo(tinfo);

        // 8.2 若设置了超时时间，添加条件定时器
        if (to != (uint64_t)-1) {
            timer = iom->addConditionTimer(
                to,  // 超时时间（毫秒）
                // 定时器回调：超时后标记状态并取消 IO 事件
                [winfo, fd, iom, event]() {
                    auto t = winfo.lock();  // 尝试获取 shared_ptr（若已释放则不执行）
                    if (!t || t->cnacelled) {
                        return;
                    }
                    t->cnacelled = ETIMEDOUT;  // 标记超时
                    iom->cancelEvent(fd, (Event)(event));  // 取消 IO 事件监听
                },
                winfo  // 传递弱指针作为条件（确保回调仅在 tinfo 有效时执行）
            );
        }

        // 8.3 向 IO 管理器注册 IO 事件（等待数据就绪）
        int rt = iom->addEvent(fd, (Event)(event));
        if (rt != 0) {  // 注册事件失败
            std::cout << hook_fun_name << " addEvent(" << fd << ", " << event << ") failed" << std::endl;
            if (timer) {
                timer->cancel();  // 取消定时器
            }
            return -1;
        } 
        else {  // 注册事件成功
            // 8.4 挂起当前协程（让出 CPU，等待事件就绪后被唤醒）
            Fiber::GetThis()->yield();

            // 8.5 协程被唤醒后，取消定时器（若存在）
            if (timer) {
                timer->cancel();
            }

            // 8.6 若超时标志被设置，返回超时错误
            if (tinfo->cnacelled) {
                errno = tinfo->cnacelled;  // 设置错误码为 ETIMEDOUT
                return -1;
            }

            // 8.7 事件就绪，跳回 retry 标签重新执行 IO 操作
            goto retry;
        }
    }

    // 9. 返回 IO 操作结果（成功时为字节数，失败时为-1）
    return n;
}

extern "C" {
#define XX(name) name##_fun name##_f = nullptr;
    HOOK_FUN(XX);
#undef XX

unsigned int sleep(unsigned int seconds) {
    if (!t_hook_enable) {
        // 未启用Hook：直接调用系统原始函数
        return sleep_f(seconds);
    }

    // 启用Hook：将线程睡眠转换为协程挂起
    Fiber::ptr fiber = Fiber::GetThis();  // 获取当前协程
    IOManager *iom = IOManager::GetThis();  // 获取IO管理器

    // 添加定时器：seconds*1000毫秒后唤醒协程
    iom->addTimer(seconds * 1000,
                std::bind((void(Scheduler::*)(Fiber::ptr, int thread)) & IOManager::scheduler, iom, fiber, -1));
    Fiber::GetThis()->yield();  // 挂起当前协程，让出CPU
    return 0;
}

int usleep(useconds_t usec) {
    if (!t_hook_enable) {
        return usleep_f(usec);  // 未启用Hook：调用原始函数
    }

    // 启用Hook：微秒级睡眠转换为协程挂起
    Fiber::ptr fiber = Fiber::GetThis();
    IOManager *iom = IOManager::GetThis();

    iom->addTimer(usec / 1000,  // 转换为毫秒
                std::bind(&IOManager::scheduler<Fiber::ptr>, iom, fiber, -1));
    Fiber::GetThis()->yield();
    return 0;
}
  
int nanosleep(const struct timespec *req, struct timespec *rem) {
    if (!t_hook_enable) {
        return nanosleep_f(req, rem);  // 未启用Hook：调用原始函数
    }

    // 启用Hook：纳秒级睡眠转换为协程挂起
    Fiber::ptr fiber = Fiber::GetThis();
    IOManager *iom = IOManager::GetThis();

    // 转换纳秒为毫秒
    int timeout_s = req->tv_sec * 1000 + req->tv_nsec / 1000 / 1000;
    iom->addTimer(timeout_s, std::bind(&IOManager::scheduler<Fiber::ptr>, iom, fiber, -1));
    Fiber::GetThis()->yield();
    return 0;
}

int socket(int domain, int type, int protocol) {
    if (!t_hook_enable) {
        return socket_f(domain, type, protocol);  // 未启用Hook：调用原始函数
    }

    // 启用Hook：创建socket后纳入FdMgr管理
    int fd = socket_f(domain, type, protocol);
    if (fd == -1) {
        return fd;
    }

    // 将新创建的socket fd加入文件描述符管理器，跟踪其状态
    FdMgr::GetInstance()->get(fd, true);
    return fd;
}

int connect_with_timeout(int fd, const struct sockaddr *addr, socklen_t addrlen, uint64_t timeout_ms) {
    if (!t_hook_enable) {
        return connect_f(fd, addr, addrlen);  // 未启用Hook：调用原始函数
    }

    // 获取fd上下文，检查有效性
    FdCtx::ptr ctx = FdMgr::GetInstance()->get(fd);
    if (!ctx || ctx->isClose() || !ctx->isSocket()) {
        errno = EBADF;
        return -1;
    }

    if (ctx->getUserNonblock()) {  // 用户已设置非阻塞，直接调用原始函数
        return connect_f(fd, addr, addrlen);
    }
  
    // 调用原始connect（此时fd被Hook内部设置为非阻塞）
    int n = connect_f(fd, addr, addrlen);
    if (n == 0) {  // 连接成功
        return 0;
    } 
    else if (n != -1 || errno != EINPROGRESS) {  // 连接失败（非正在进行）
        return n;
    }
  
    // 连接正在进行（EINPROGRESS），进入协程等待
    IOManager* iom = IOManager::GetThis();
    Timer::ptr timer;  // 超时定时器
    std::shared_ptr<timer_info> tinfo(new timer_info);  // 跟踪超时状态
    std::weak_ptr<timer_info> winfo(tinfo);  // 弱指针避免循环引用
  
    // 设置超时定时器
    if (timeout_ms != (uint64_t)-1) {
        timer = iom->addConditionTimer(
            timeout_ms,
            [winfo, fd, iom]() {  // 超时回调：标记超时并取消事件
                auto t = winfo.lock();
                if (t && !t->cnacelled) {
                    t->cnacelled = ETIMEDOUT;
                    iom->cancelEvent(fd, WRITE);
                }
            },
            winfo
        );
    }
  
    // 注册WRITE事件（连接完成时触发），挂起协程
    int rt = iom->addEvent(fd, WRITE);
    if (rt == 0) {
        Fiber::GetThis()->yield();  // 等待连接完成或超时
        if (timer) timer->cancel();  // 取消定时器
  
        if (tinfo->cnacelled) {  // 超时
            errno = tinfo->cnacelled;
            return -1;
        }
    } 
    else {  // 事件注册失败
        if (timer) timer->cancel();
        std::cout << "connect addEvent error" << std::endl;
    }
  
    // 检查连接结果（通过SO_ERROR获取错误状态）
    int error = 0;
    socklen_t len = sizeof(int);
    if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) == -1) {
        return -1;
    }
    return error ? (errno = error, -1) : 0;  // 成功返回0，失败设置errno
}

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
    return fiber::connect_with_timeout(sockfd, addr, addrlen, s_connect_timeout);
}

int accept(int s, struct sockaddr *addr, socklen_t *addrlen) {
    int fd = do_io(s, accept_f, "accept", READ, SO_RCVTIMEO, addr, addrlen);
    if (fd >= 0) {
        FdMgr::GetInstance()->get(fd, true);
    }
    return fd;
}

ssize_t read(int fd, void *buf, size_t count) { return do_io(fd, read_f, "read", READ, SO_RCVTIMEO, buf, count); }

ssize_t readv(int fd, const struct iovec *iov, int iovcnt) {
    return do_io(fd, readv_f, "readv", READ, SO_RCVTIMEO, iov, iovcnt);
}

ssize_t recv(int sockfd, void *buf, size_t len, int flags) {
    return do_io(sockfd, recv_f, "recv", READ, SO_RCVTIMEO, buf, len, flags);
}

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen) {
  return do_io(sockfd, recvfrom_f, "recvfrom", READ, SO_RCVTIMEO, buf, len, flags, src_addr, addrlen);
}

ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags) {
    return do_io(sockfd, recvmsg_f, "recvmsg", READ, SO_RCVTIMEO, msg, flags);
}

ssize_t write(int fd, const void *buf, size_t count) {
    return do_io(fd, write_f, "write", WRITE, SO_SNDTIMEO, buf, count);
}

ssize_t writev(int fd, const struct iovec *iov, int iovcnt) {
    return do_io(fd, writev_f, "writev", WRITE, SO_SNDTIMEO, iov, iovcnt);
}

ssize_t send(int s, const void *msg, size_t len, int flags) {
    return do_io(s, send_f, "send", WRITE, SO_SNDTIMEO, msg, len, flags);
}

ssize_t sendto(int s, const void *msg, size_t len, int flags, const struct sockaddr *to, socklen_t tolen) {
    return do_io(s, sendto_f, "sendto", WRITE, SO_SNDTIMEO, msg, len, flags, to, tolen);
}

ssize_t sendmsg(int s, const struct msghdr *msg, int flags) {
    return do_io(s, sendmsg_f, "sendmsg", WRITE, SO_SNDTIMEO, msg, flags);
}

int close(int fd) {
    if (!t_hook_enable) {
        return close_f(fd);
    }

    FdCtx::ptr ctx = FdMgr::GetInstance()->get(fd);
    if (ctx) {
        auto iom = IOManager::GetThis();
        if (iom) {
            iom->cancelAll(fd);
        }
        FdMgr::GetInstance()->del(fd);
    }
    return close_f(fd);
}

int fcntl(int fd, int cmd, ... /* arg */) {
    va_list va;
    va_start(va, cmd);
    switch (cmd) {
    case F_SETFL: {
        int arg = va_arg(va, int);
        va_end(va);
        FdCtx::ptr ctx = FdMgr::GetInstance()->get(fd);
        if (!ctx || ctx->isClose() || !ctx->isSocket()) {
            return fcntl_f(fd, cmd, arg);
        }
        ctx->setUserNonblock(arg & O_NONBLOCK);
        if (ctx->getSysNonblock()) {
            arg |= O_NONBLOCK;
        } 
        else {
            arg &= ~O_NONBLOCK;
        }
        return fcntl_f(fd, cmd, arg);
    } break;
    case F_GETFL: {
        va_end(va);
        int arg = fcntl_f(fd, cmd);
        FdCtx::ptr ctx = FdMgr::GetInstance()->get(fd);
        if (!ctx || ctx->isClose() || !ctx->isSocket()) {
            return arg;
        }
        if (ctx->getUserNonblock()) {
            return arg | O_NONBLOCK;
        } 
        else {
            return arg & ~O_NONBLOCK;
        }
    } break;
    case F_DUPFD:
    case F_DUPFD_CLOEXEC:
    case F_SETFD:
    case F_SETOWN:
    case F_SETSIG:
    case F_SETLEASE:
    case F_NOTIFY:
#ifdef F_SETPIPE_SZ
    case F_SETPIPE_SZ:
#endif
    {
        int arg = va_arg(va, int);
        va_end(va);
        return fcntl_f(fd, cmd, arg);
    } break;
    case F_GETFD:
    case F_GETOWN:
    case F_GETSIG:
    case F_GETLEASE:
#ifdef F_GETPIPE_SZ
    case F_GETPIPE_SZ:
#endif
    {
        va_end(va);
        return fcntl_f(fd, cmd);
    } break;
    case F_SETLK:
    case F_SETLKW:
    case F_GETLK: {
        struct flock *arg = va_arg(va, struct flock *);
        va_end(va);
        return fcntl_f(fd, cmd, arg);
    } break;
    case F_GETOWN_EX:
    case F_SETOWN_EX: {
        struct f_owner_exlock *arg = va_arg(va, struct f_owner_exlock *);
        va_end(va);
        return fcntl_f(fd, cmd, arg);
    } break;
    default:
        va_end(va);
        return fcntl_f(fd, cmd);
    }
}

int ioctl(int d, unsigned long int request, ...) {
    va_list va;
    va_start(va, request);
    void *arg = va_arg(va, void *);
    va_end(va);

    if (FIONBIO == request) {
        bool user_nonblock = !!*(int *)arg;
        FdCtx::ptr ctx = FdMgr::GetInstance()->get(d);
        if (!ctx || ctx->isClose() || !ctx->isSocket()) {
            return ioctl_f(d, request, arg);
        }
        ctx->setUserNonblock(user_nonblock);
    }
    return ioctl_f(d, request, arg);
}

int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen) {
    return getsockopt_f(sockfd, level, optname, optval, optlen);
}

int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen) {
    if (!t_hook_enable) {
        return setsockopt_f(sockfd, level, optname, optval, optlen);
    }
    if (level == SOL_SOCKET) {
        if (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO) {
            FdCtx::ptr ctx = FdMgr::GetInstance()->get(sockfd);
            if (ctx) {
                const timeval *v = (const timeval *)optval;
                ctx->setTimeout(optname, v->tv_sec * 1000 + v->tv_usec / 1000);
            }
        }
    }
    return setsockopt_f(sockfd, level, optname, optval, optlen);
}

}

}  // namespace fiber